﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;

namespace Qreed.Collections
{
    /// <summary>
    /// Collection class that allows to register events on changes.
    /// </summary>
    /// <typeparam name="T">The type of objects in the collection.</typeparam>
    public class NotifyOnChangeCollection<T> : Collection<T>
    {
        /// <summary>
        /// Occurs when the collection has been changed.
        /// </summary>
        public event EventHandler<NotifyOnChangeEventArgs<T>> OnChange;

        /// <summary>
        /// Inserts an element into the <see cref="T:System.Collections.ObjectModel.Collection`1"/> at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
        /// <param name="item">The object to insert. The value can be null for reference types.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// 	<paramref name="index"/> is less than zero.
        /// -or-
        /// <paramref name="index"/> is greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.
        /// </exception>
        protected override void InsertItem(int index, T item)
        {
            base.InsertItem(index, item);

            if (OnChange != null)
            {
                NotifyOnChangeEventArgs<T> e = new NotifyOnChangeEventArgs<T>();

                e.Index = index;
                e.Item = item;
                e.EventType = NotifyOnChangeEventType.InsertItem;

                OnChange(this, e);
            }
        }

        /// <summary>
        /// Removes all elements from the <see cref="T:System.Collections.ObjectModel.Collection`1"/>.
        /// </summary>
        protected override void ClearItems()
        {
            base.ClearItems();

            if (OnChange != null)
            {
                NotifyOnChangeEventArgs<T> e = new NotifyOnChangeEventArgs<T>();

                e.EventType = NotifyOnChangeEventType.ClearItems;

                OnChange(this, e);
            }
        }

        /// <summary>
        /// Removes the element at the specified index of the <see cref="T:System.Collections.ObjectModel.Collection`1"/>.
        /// </summary>
        /// <param name="index">The zero-based index of the element to remove.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// 	<paramref name="index"/> is less than zero.
        /// -or-
        /// <paramref name="index"/> is equal to or greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.
        /// </exception>
        protected override void RemoveItem(int index)
        {
            base.RemoveItem(index);

            if (OnChange != null)
            {
                NotifyOnChangeEventArgs<T> e = new NotifyOnChangeEventArgs<T>();

                e.Index = index;
                e.EventType = NotifyOnChangeEventType.RemoveItem;

                OnChange(this, e);
            }
        }

        /// <summary>
        /// Replaces the element at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index of the element to replace.</param>
        /// <param name="item">The new value for the element at the specified index. The value can be null for reference types.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">
        /// 	<paramref name="index"/> is less than zero.
        /// -or-
        /// <paramref name="index"/> is greater than <see cref="P:System.Collections.ObjectModel.Collection`1.Count"/>.
        /// </exception>
        protected override void SetItem(int index, T item)
        {
            base.SetItem(index, item);

            if (OnChange != null)
            {
                NotifyOnChangeEventArgs<T> e = new NotifyOnChangeEventArgs<T>();

                e.Index = index;
                e.Item = item;
                e.EventType = NotifyOnChangeEventType.SetItem;

                OnChange(this, e);
            }
        }
    }

    /// <summary>
    /// Indicates the type of change event that has occured.
    /// </summary>
    public enum NotifyOnChangeEventType
    {
        /// <summary>
        /// Unknown event.
        /// </summary>
        Unknown,
        /// <summary>
        /// An item is inserted.
        /// </summary>
        InsertItem,
        /// <summary>
        /// All items are cleared.
        /// </summary>
        ClearItems,
        /// <summary>
        /// An item is removed.
        /// </summary>
        RemoveItem,
        /// <summary>
        /// An item is replaced.
        /// </summary>
        SetItem
    }

    /// <summary>
    /// Event arguments passed when the collection has been modified.
    /// </summary>
    /// <typeparam name="T">The type of objects in the collection.</typeparam>
    public class NotifyOnChangeEventArgs<T> : EventArgs
    {
        private NotifyOnChangeEventType _eventType = NotifyOnChangeEventType.Unknown;
        /// <summary>
        /// Gets or sets the type of the event.
        /// </summary>
        /// <value>The type of the event.</value>
        public NotifyOnChangeEventType EventType
        {
            get { return this._eventType; }
            set { this._eventType = value; }
        }

        private int _index = -1;
        /// <summary>
        /// Gets or sets the index.
        /// </summary>
        /// <value>The index.</value>
        public int Index
        {
            get { return this._index; }
            set { this._index = value; }
        }

        private T _item = default(T);
        /// <summary>
        /// Gets or sets the item.
        /// </summary>
        /// <value>The item.</value>
        public T Item
        {
            get { return this._item; }
            set { this._item = value; }
        }
    }
}
